package rede;

import java.util.ArrayList;
import java.util.Random;

import funcoesAtivacao.SigmoideLogistica;
import Configuracoes.Configs;
import Neuronio.Camada;
import Util.Arquivo;
import Util.Padrao;

public class RedeMLP {
	private Camada camadaEntrada;
	private Camada camadaEscondida;
	private Camada camadaSaida;
	private int quantidadeCiclos = Configs.QUANTIDADE_CICLOS;
	private int cicloAtual = 1;
	
	
	
	public RedeMLP()
	{
		camadaEntrada = new Camada(Configs.QUANTIDADE_NEURONIOS_CAMADA_ENTRADA , Configs.QUANTIDADE_ENTRADAS , new SigmoideLogistica());
		
		camadaEscondida = new Camada(Configs.QUANTIDADE_NEURONIOS_CAMADA_ESCONDIDA , Configs.QUANTIDADE_NEURONIOS_CAMADA_ENTRADA , new SigmoideLogistica());
		
		camadaSaida = new Camada(Configs.QUANTIDADE_SAIDAS,Configs.QUANTIDADE_NEURONIOS_CAMADA_ESCONDIDA , new SigmoideLogistica() );
	}
	
	public int getQuantidadeCiclos()
	{
		return this.quantidadeCiclos;
	}
	
	public Camada getCamadaEntrada() {
		return camadaEntrada;
	}

	public void setCamadaEntrada(Camada camadaEntrada) {
		this.camadaEntrada = camadaEntrada;
	}

	public Camada getCamadaEscondida() {
		return camadaEscondida;
	}

	public void setCamadaEscondidaa(Camada camadaEscondida) {
		this.camadaEscondida = camadaEscondida;
	}
	
	public void calcularForward(Padrao padrao)
	{
		double[] entradasCamadaEscondida = new double[camadaEntrada.getNeuronios().length];
		double[] saidas = new double[Configs.QUANTIDADE_NEURONIOS_CAMADA_ESCONDIDA];
		
		
		for (int i=0; i < camadaEntrada.getNeuronios().length; i++)
		{
			camadaEntrada.getNeuronios()[i].setEntradas(padrao.entradas);
			entradasCamadaEscondida[i] = camadaEntrada.getNeuronios()[i].calcularSaidaForward();
		}
		
		for(int i = 0; i < camadaEscondida.getNeuronios().length; i++)
		{
			camadaEscondida.getNeuronios()[i].setEntradas(entradasCamadaEscondida);
			saidas[i] = camadaEscondida.getNeuronios()[i].calcularSaidaForward();
		}
		for(int i = 0; i<camadaSaida.getNeuronios().length;i++)
		{
			camadaSaida.getNeuronios()[i].setEntradas(saidas);
			camadaSaida.getNeuronios()[i].calcularSaidaForward();
		}
	}
	
	

	
	
	public void rodarRede()
	{
		Arquivo arq = new Arquivo(Configs.ARQUIVO);
		ArrayList<Padrao> padroes = arq.lerArquivo();
		ArrayList<Padrao> padroesTreinamento = new ArrayList<Padrao>();
		int tamanhoTreinamento = padroes.size()/2;
		Random rand = new Random();
		//separando conjunto treinamento
		for(int i = 0; i < tamanhoTreinamento; i++)
		{
			int r = rand.nextInt(padroes.size());
			padroesTreinamento.add(padroes.remove(r));
		}
		
		for(int i = 0; i < tamanhoTreinamento; i++)
		{
			realizarPasso(padroes.get(i));
			
		}
		//fase validacaoCruzada
		
		
		//fase teste
		
		
	}
	
	
	public void realizarPasso(Padrao padrao)
	{
		//fase forward
		calcularForward(padrao);
		
		//fase backward
		//camada saida
		//calculando erro
		double[] erroIndividualCamadaSaida = calcularErroIndividualCamadaSaida(padrao);
		//passo de ajuste de pesos
		//calculando sensibilidade
		double[] sensibilidadeCamadaSaida = calcularSensibilidadeCamadaSaida(erroIndividualCamadaSaida);
		//ajustando os pesos
		ajustarPesosCamadaSaida(sensibilidadeCamadaSaida);
		
		//camada escondida
		//calculando sensibilidade
		double[] sensibilidadeCamadaEscondida =  calcularSensibilidadeCamadaEscondida(sensibilidadeCamadaSaida);
		
		//atualizando pesos
		ajustarPesosCamadaEscondida(sensibilidadeCamadaEscondida);
		
		double erroMedioQuadratico = 0;
		for(int i = 0; i < 3; i++)
		{
			erroMedioQuadratico += Math.pow(erroIndividualCamadaSaida[i], 2);
		}
		System.out.println(erroMedioQuadratico/3);
	}
	
	
	//camada saida
	
	public double[] calcularErroIndividualCamadaSaida(Padrao padrao)
	{
		double erro[] =  new double[3];

		for(int i = 0; i < Configs.QUANTIDADE_SAIDAS; i++)
		{
			erro[i] = padrao.saidas[i] - camadaSaida.getNeuronios()[i].getSaida(); 
		}
		return erro;
	}
	
	
	
	public double[] calcularSensibilidadeCamadaSaida(double[] erroIndividualCamadaSaida)
	{
		double [] sensibilidadesCamadaSaida = new double[Configs.QUANTIDADE_SAIDAS];
		double net;
		for(int i = 0; i < Configs.QUANTIDADE_SAIDAS; i++)
		{
			net = 0;
			for(int j = 0; j < camadaSaida.getNeuronios()[i].getEntradas().length; j++)
			{
				net += camadaSaida.getNeuronios()[i].getEntradas()[j] * camadaSaida.getNeuronios()[i].getPesos()[j]; 
			}
			sensibilidadesCamadaSaida[i] = camadaSaida.getNeuronios()[i].calcularSaidaBackward(net) * erroIndividualCamadaSaida[i];
		}
		return sensibilidadesCamadaSaida;
	}
	
	public void ajustarPesosCamadaSaida(double[] sensibilidadeCamadaSaida)
	{
		for(int i = 0; i < Configs.QUANTIDADE_SAIDAS; i++)
		{
			for(int j = 0; j < Configs.QUANTIDADE_NEURONIOS_CAMADA_ESCONDIDA; j++)
			{
				camadaSaida.getNeuronios()[i].getPesos()[j] += Configs.TAXA_APRENDIZAGEM * sensibilidadeCamadaSaida[i] * camadaSaida.getNeuronios()[i].getSaida(); 
			}
		}
	}
	//-------------------------------------------------------------------------
	//-----------------------------camada escondida----------------------------
	//-------------------------------------------------------------------------
	public double[] calcularSensibilidadeCamadaEscondida(double[] sensibilidadesCamadaSaida)
	{
		double net = 0;
		double[] sensibilidadesCamadaEscondida = new double[Configs.QUANTIDADE_NEURONIOS_CAMADA_ESCONDIDA];
		
		double[] sensibilidadeAcumulada = new double[Configs.QUANTIDADE_NEURONIOS_CAMADA_ESCONDIDA];
		
		for(int i = 0; i < Configs.QUANTIDADE_NEURONIOS_CAMADA_ESCONDIDA; i++)
		{
			for(int j = 0; i < Configs.QUANTIDADE_SAIDAS; j++)
			{
				sensibilidadeAcumulada[i] += camadaSaida.getNeuronios()[j].getPesos()[i] * sensibilidadesCamadaSaida[i];
			}
		}
		
		
		
//		
//		
//		for(int i = 0; i < Configs.QUANTIDADE_NEURONIOS_CAMADA_ESCONDIDA; i++)
//		{
//			sensibilidadeAcumulada[i] = 0;
//			net = 0;
//			
//			for(int j = 0; j < camadaEscondida.getNeuronios()[i].getEntradas().length; j++)
//			{
//				net += camadaEscondida.getNeuronios()[i].getEntradas()[j] * camadaEscondida.getNeuronios()[i].getPesos()[j];
//			}
//			
//			for(int j = 0; j < sensibilidadesCamadaSaida.length; j++)
//			{
//				sensibilidadeAcumulada[i] += camadaSaida.getNeuronios()[i].getPesos()[j] * sensibilidadesCamadaSaida[j];
//			}
//			
//			sensibilidadesCamadaEscondida[i] +=  camadaEscondida.getNeuronios()[i].calcularSaidaBackward(net) * sensibilidadeAcumulada[i];
//		}
		return sensibilidadesCamadaEscondida;
	}
	
	
	
	public void ajustarPesosCamadaEscondida(double[] sensibilidadeCamadaEscondida)
	{
		for(int i = 0; i < Configs.QUANTIDADE_NEURONIOS_CAMADA_ESCONDIDA; i++)
		{
			for(int j = 0; j < Configs.QUANTIDADE_NEURONIOS_CAMADA_ENTRADA; j++)
			{
				camadaEscondida.getNeuronios()[i].getPesos()[j] += Configs.TAXA_APRENDIZAGEM * sensibilidadeCamadaEscondida[i] * camadaEscondida.getNeuronios()[i].getSaida(); 
			}
		}
	}
	
	
}
